package com.thghh.ch1.bignumber;
import	java.io.StringWriter;

/**
 * @author Zhikang.Peng
 * @version 1.0
 * @email thghh@qq.com
 * @date 2020/7/1 11:21
 */
public class BigNumber extends Number implements Comparable<BigNumber>{

    public static final BigNumber ZERO = new BigNumber(new int[0], 0);

    /**
     * The signum of this BigInteger: -1 for negative, 0 for zero, or
     * 1 for positive.  Note that the BigInteger zero <i>must</i> have
     * a signum of 0.  This is necessary to ensures that there is exactly one
     * representation for each BigInteger value.
     *
     * @serial
     */
    private final int signum;

    /**
     * The magnitude of this BigInteger, in <i>big-endian</i> order: the
     * zeroth element of this array is the most-significant int of the
     * magnitude.  The magnitude must be "minimal" in that the most-significant
     * int ({@code mag[0]}) must be non-zero.  This is necessary to
     * ensure that there is exactly one representation for each BigInteger
     * value.  Note that this implies that the BigInteger zero has a
     * zero-length mag array.
     */
    private  final int[] mag;

    public BigNumber(int[] magnitude, int signum) {
        this.signum = (magnitude.length == 0 ? 0 : signum);
        this.mag = magnitude;
    }

    public BigNumber(String val) {
        int cursor = 0, numDigits;
        final int len = val.length();

        if (len == 0)
            throw new NumberFormatException("Zero length BigInteger");

        // Check for at most one leading sign
        int sign = 1;
        int index1 = val.lastIndexOf('-');
        int index2 = val.lastIndexOf('+');
        if (index1 >= 0) {
            if (index1 != 0 || index2 >= 0) {
                throw new NumberFormatException("Illegal embedded sign character");
            }
            sign = -1;
            cursor = 1;
        } else if (index2 >= 0) {
            if (index2 != 0) {
                throw new NumberFormatException("Illegal embedded sign character");
            }
            cursor = 1;
        }
        if (cursor == len)
            throw new NumberFormatException("Zero length BigInteger");

        // Skip leading zeros and compute number of digits in magnitude
        while (cursor < len &&
                Character.digit(val.charAt(cursor), 10) == 0) {
            cursor++;
        }

        if (cursor == len) {
            signum = 0;
            mag = ZERO.mag;
            return;
        }
        signum = sign;
        mag = new int[len - cursor];
        int index = 0;
        while (cursor < len) {
            char c = val.charAt(cursor++);
            int cVal = Character.digit(c, 10);
            mag[index++] = cVal;
        }
    }

    /**
     * Compares the magnitude array of this BigInteger with the specified
     * BigInteger's. This is the version of compareTo ignoring sign.
     *
     * @param val BigInteger whose magnitude array to be compared.
     * @return -1, 0 or 1 as this magnitude array is less than, equal to or
     *         greater than the magnitude aray for the specified BigInteger's.
     */
    final int compareMagnitude(BigNumber val) {
        int[] m1 = mag;
        int len1 = m1.length;
        int[] m2 = val.mag;
        int len2 = m2.length;
        if (len1 < len2)
            return -1;
        if (len1 > len2)
            return 1;
        for (int i = 0; i < len1; i++) {
            int a = m1[i];
            int b = m2[i];
            if (a != b)
                return ((a < b)) ? -1 : 1;
        }
        return 0;
    }

    public int compareTo(BigNumber val) {
        if (signum == val.signum) {
            switch (signum) {
                case 1:
                    return compareMagnitude(val);
                case -1:
                    return val.compareMagnitude(this);
                default:
                    return 0;
            }
        }
        return signum > val.signum ? 1 : -1;
    }

    public int intValue() {
        return 0;
    }

    public long longValue() {
        return 0;
    }

    public float floatValue() {
        return 0;
    }

    public double doubleValue() {
        return 0;
    }

    public BigNumber add(BigNumber val) {
        if (val.signum == 0)
            return this;
        if (signum == 0)
            return val;

            return new BigNumber(add(mag, val.mag), signum);
    }

    public int[] add(int[] x, int[] y) {
        // If x is shorter, swap the two arrays
        if (x.length < y.length) {
            int[] tmp = x;
            x = y;
            y = tmp;
        }
        int xIndex = x.length;
        int yIndex = y.length;
        int[] result = new int[xIndex];
        long sum = 0;
        if(yIndex == 0) {
            sum = x[--xIndex] + y[0];
            result[xIndex] = (int)sum;
        } else {
            while(yIndex > 0) {
                sum = x[--xIndex] + y[--yIndex] + (sum / 10);
                result[xIndex] = (int)(sum % 10);
            }
        }

        // Copy remainder of longer number while carry propagation is required
        boolean carry = (sum / 10 != 0);
        while (xIndex > 0 && carry)
            carry = ((result[--xIndex] = (x[xIndex] + 1) % 10) == 0);

        // Copy remainder of longer number
        while (xIndex > 0)
            result[--xIndex] = x[xIndex];

        // Grow result if necessary
        if (carry) {
            int bigger[] = new int[result.length + 1];
            System.arraycopy(result, 0, bigger, 1, result.length);
            bigger[0] = 0x01;
            return bigger;
        }
        return result;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(signum == -1 ? "-" : "");
        for (int i=0; i<mag.length;i++) {
            sb.append(mag[i]);
        }
        return sb.toString();
    }
}
